package com.ysh.smartplatform.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ysh.smartplatform.exceptions.MyBaseException;
import com.ysh.smartplatform.log.annotations.LogAnnotation;
import com.ysh.smartplatform.pojo.BaseUser;
import com.ysh.smartplatform.pojo.Operlog;
import com.ysh.smartplatform.pojo.SysUserInfo;

import com.ysh.smartplatform.service.OperLogService;
import com.ysh.smartplatform.utils.RequestUtil;
import com.ysh.smartplatform.utils.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 袁胜虎 on 2022/9/14 19:35
 */
@Aspect//强化通知
@Component
public class LogAdvice {
    private OperLogService operLogService;

    @Autowired
    public void setOperLogService(OperLogService operLogService) {
        this.operLogService = operLogService;
    }

    private ObjectMapper objectMapper;

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /**
     * 指定我们要拦截的方法,我们想要拦截的方法上面是带有LogAnnotation注解的
     * 用于匹配方法执行的连接点
     */
    @Pointcut("@annotation(com.ysh.smartplatform.log.annotations.LogAnnotation)")
    public void pointCut(){

    }
    /**
     * 后置通知
     * @param joinPoint
     * @param result 方法的返回结果
     */
    @AfterReturning(pointcut = "pointCut()",returning = "result")
    public void after(JoinPoint joinPoint,Object result){
//        //假如在这里我们可以获取到请求对象,则就不需要前置通知了,在这里直接生成好数据就行了
//        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//        //获取到当前正在被拦截的方法对象,可以从里面获取到方法的名字和参数
//        Method method = signature.getMethod();
//        //我们的标题,业务类型都在方法的注解上面,现在我们有方法了,可以获取到注解
//        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
//        String title = logAnnotation.title();
//        int business_type = logAnnotation.business_type();
//        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
//        //请求相关的数据可以从request中获取,比如地址, 请求方式,ip,访问的客户端类型,
//        //传递的参数其实就是方法的参数,我们可以将方法的参数获取到,然后转成数据保存过去,但是我们的参数可能会有多个,但是在表中我们只有一个列,怎么用一个列来保存所有的值?
//        //我们可以将这些参数转成json,放到一个map中转成json
//        //获取数据,保存到数据库
//        System.err.println(title+"-------->"+business_type);
        Operlog operlog = new Operlog();
        try {
            operlog.setJsonResult(objectMapper.writeValueAsString(result));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        operlog.setStatus(1);
        operlog.setErrorMsg("200 OK");
        addLog(operlog,joinPoint);
    }

    @AfterThrowing(pointcut = "pointCut()" ,throwing = "exp")
    public void afterThrowing(JoinPoint joinPoint,Exception exp){
        //假如在这里我们可以获取到请求对象,则就不需要前置通知了,在这里直接生成好数据就行了
        Operlog operlog = new Operlog();
        operlog.setStatus(0);
        if (exp instanceof MyBaseException){
            operlog.setErrorMsg(((MyBaseException) exp).getCode()+","+exp.getMessage());
        }else {
            operlog.setErrorMsg(exp.getMessage());
        }
        addLog(operlog,joinPoint);
    }

    public void addLog(Operlog operlog,JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        String title = logAnnotation.title();
        int business_type = logAnnotation.business_type();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        System.err.println(title + "=----->" + business_type);

        String ip = RequestUtil.getRemoteHost(request);
        String locationByIp = RequestUtil.getLocationByIp(ip);
        BaseUser user = SecurityUtils.getLoginUser();
        String requestURI = request.getRequestURI();
        Map<String,String> osAndBrowserInfo = RequestUtil.getOsAndBrowserInfo(request);
        String methodName = method.getName();
        Object[] args = joinPoint.getArgs();

        Parameter[] parameters = method.getParameters();
        HashMap<Object,Object> paramterMap = new HashMap<>();
        for (int i = 0; i <parameters.length ; i++) {
            Parameter parameter = parameters[i];
            Object value = args[i];
            paramterMap.put(parameter.getName(),value);
        }

        operlog.setTitle(title);
        operlog.setBusinessType(business_type);
        operlog.setMethod(methodName);
        operlog.setOperTime(new Date());

        String os = osAndBrowserInfo.get("os");

        switch (os){
            case "Android":
            case "IPhone":
                operlog.setOperatorType(2);
                break;
            case "Windows":
            case "Mac":
            case "Unix":
                operlog.setOperatorType(1);
                break;
            default:
                operlog.setOperatorType(0);
                break;
        }

        operlog.setOperName(user.getUsername());
        operlog.setOperUrl(requestURI);
        operlog.setOperIp(ip);
        operlog.setOperLocation(locationByIp);
        try {
            operlog.setOperParam(objectMapper.writeValueAsString(paramterMap));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        System.err.println(operlog);
        operLogService.addOperLog(operlog);
    }
}
